#include "smartbox.h"

using namespace std::chrono; 
  
namespace app {

SmartBox::SmartBox(ConfigInfo* cfg_ifo, LogInfo* log_ifo):
  log_ifo(log_ifo), rain_value(0), is_post(0), act_code(nullptr),
  cfg_ifo(cfg_ifo) {

#ifdef __arm__
	act_code = new ActivateCode(check_orangepi);
#endif
#ifdef __aarch64__
	act_code = new ActivateCode(check_orangepi3);
#endif
#ifdef __x86_64__
	act_code = new ActivateCode(check_x86_linux);
#endif
  if (act_code) {act_code->set_lock_path("./lock.t");}
  mdb_api = new ModbusAPI();

}

SmartBox::~SmartBox() {}

int SmartBox::get_rain_value() {
  int value = 0;
  rain_mut.lock();
  value = rain_value;
  rain_mut.unlock();
  return value;
}

int SmartBox::run() {
  int ret = 0;
  if (act_code == nullptr) {
    spdlog::get("logger")->info("ERROR. SmartBox::run act_code==nullptr");
    return -1;
  }
  // 检查是否注册
  if (cfg_ifo->cameraGroups.size() == 0) { 
    check_register(log_ifo, cfg_ifo, act_code);
  } 

  std::thread(&SmartBox::post_video_thread, this).detach();

  int64 heart_time=0,post_time=0, cur_time=0, query_time=0, rtu_time=0;
  query_time = heart_time = post_time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
  int64 reboot_time = 0; 

  int logger_flag = 0;
  while (true) {
    std::this_thread::sleep_for(milliseconds(50));
    cur_time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();

    if (cur_time - rtu_time > 5 * 1e3) {
      rtu_time = cur_time;
      rain_mut.lock();
      ret = mdb_api->read_data_rtu(rain_value);
      if (ret != 0 ) { rain_value = -1;}
      rain_mut.unlock();
    }

    // post data
    if (cur_time - post_time > 5 * 1e3 && is_post == 0) {
      if (log_ifo->log_level_2) spdlog::get("logger")->info("SmartBox::post data...");
      std::thread(&SmartBox::post_data, this, cur_time).detach();
      post_time = cur_time;
    }

    // heart
    if (cur_time - heart_time > 30 * 1e3) {
      // http heart
      // send_http_heart(log_ifo, cfg_ifo);
      send_local_heart(log_ifo, cfg_ifo);
      heart_time = cur_time;
    }

    // update config // 检查是否过期
    if (cur_time - query_time > 1 * 60 * 1e3) {
      query_time = cur_time;
      ret = query_device(log_ifo, cfg_ifo, act_code, "");
      if (log_ifo->log_level_4) spdlog::get("logger")->info("SmartBox::run query_device ret:{}, cam_mngs:{}", ret, cam_mngs.size());
      // 过期
      if (ret != 0 && !cam_mngs.empty()) { 
        for (auto cm : cam_mngs) { cm->stop(); delete cm; cm = nullptr; }
        cam_mngs.clear();
      }
      // 未审批
      else if (ret != 0 && cam_mngs.empty()) {
        if (log_ifo->log_level_2) spdlog::get("logger")->info("SmartBox::run query_device error. ret:{}", ret);
      }
      // 审批通过 续期成功
      else if (ret == 0 && cam_mngs.empty()) {
        if (log_ifo->log_level_4) spdlog::get("logger")->info("SmartBox::run new CameraManager");
        for (auto& cam_group : cfg_ifo->cameraGroups) {
          cam_mngs.push_back(std::move(new CameraManager(this, &cam_group, log_ifo)));
          // remove_file(cfg_ifo->mediaDir + "/www/record/" + cam_group.cam_ifo.cameraId + "/1", 2 * 60 * 60, ".mp4");
          std::thread(&CameraManager::run, cam_mngs.back()).detach();
          if (log_ifo->log_level_4) spdlog::get("logger")->info("SmartBox::run CameraManager::run");
        }
      }
      // 正常运行
      else if (ret == 0 && !cam_mngs.empty()) {
        if (log_ifo->log_level_2) spdlog::get("logger")->info("SmartBox::run query_device ok. ret:{}", ret);
      }
    }

    // 凌晨1点刷新每天的日志路径
    int hour = (cur_time / 1000 % 86400) / 3600 + 8;
    hour = hour >= 24 ? hour - 24 : hour;
    if (!logger_flag && hour == 0) { 
      logger_flag = 1;
      spdlog::drop("logger");
      auto logger = spdlog::basic_logger_mt("logger", boost::filesystem::path(log_ifo->get_log_path()).append("log_" + get_today_date(false) + ".txt").string());
      remove_file(log_ifo->log_root, cfg_ifo->historyDay * 24* 60* 60, "");
    }
    if (hour == 1 && logger_flag) { logger_flag = 0;}
  }
  return ret;
}

void SmartBox::add_post_info(PostInfo pi, int post_type) {
  // only_video 用于上传视频失败时重复添加
  post_mut.lock();
  // 分离文字与视频上传任务
  if (post_type == onlyPostText || post_type == onlyPostIV){
    post_ifos_text.insert(post_ifos_text.end(), std::make_pair(pi.end_time == 0 ? pi.time : pi.end_time, pi));
  }
  post_mut.unlock();
  post_video_mut.lock();
  if ((post_type == onlyPostVideo || post_type == onlyPostIV) && pi.algo_type != "uploadImg" && pi.algo_type != "PeopleTrafficFlow") {
    post_ifos_video.insert(post_ifos_video.end(), std::make_pair(pi.end_time == 0 ? pi.time : pi.end_time, pi));
  }
  post_video_mut.unlock();
}

void SmartBox::post_data(int64 end_time) {
  if (is_post) { return; }
  is_post = 1;
  // 只上传文字信息
  std::multimap<int64, PostInfo> cur_post_ifos;
  post_mut.lock();
  for (auto it = post_ifos_text.begin(); it != post_ifos_text.end();) {
    // 文字资源不需要判断时间
    // if (it->first > cur_time) { break;}
    cur_post_ifos.insert(cur_post_ifos.end(), *it);
    it = post_ifos_text.erase(it);
  }
  post_mut.unlock();

  if (cfg_ifo->configVersion == "v2")  { post_data_text_v2(log_ifo, cfg_ifo, cur_post_ifos); }
  is_post = 0;
}


void SmartBox::post_video_thread() {

  int64 post_time=0, cur_time=0;
  std::multimap<int64, PostInfo> cur_post_ifos, for_train_ifos;

  while (true) {
    std::this_thread::sleep_for(milliseconds(30));
    cur_time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
    if (cur_time - post_time < 2000) { continue;}

    // 上传报警信息
    cur_post_ifos.clear();
    for_train_ifos.clear();
    post_video_mut.lock();
    for (auto it = post_ifos_video.begin(); it != post_ifos_video.end();) {
      // 视频资源需要在60s后开始上传
      if (cur_time - it->first < 60 * 1e3) { it++; continue;}
      std::string sub_str = "www/record/" + it->second.cam_grp->cam_ifo.cameraId + "/1";
      if (!it->second.cam_grp->nvr_ifo.ok && 0 != check_mp4_save_over(log_ifo, it->first, cfg_ifo->mediaDir, sub_str)) {
        it++; continue;
      }
      if (it->second.alarm < 0) {
        for_train_ifos.insert(for_train_ifos.end(), *it);
      } else {
        cur_post_ifos.insert(cur_post_ifos.end(), *it);
      }
      it = post_ifos_video.erase(it);
    }
    post_video_mut.unlock();

    if (cfg_ifo->configVersion == "v2") { 
      // 先上传图片 再上传视频
      post_data_video_v2(cur_post_ifos, onlyPostImage); 
      post_data_video_v2(cur_post_ifos, onlyPostVideo); 
    }

    // 单独保存训练数据
    for (auto it = for_train_ifos.begin(); it != for_train_ifos.end();it++) {
      PostInfo& pi = it->second;
      std::string writer_path;
      save_video_by_kit(log_ifo, pi, cfg_ifo->mediaDir, writer_path);
    }
  }
  return;
}

void SmartBox::post_data_video_v2(std::multimap<int64, PostInfo>& cur_post_ifos, PostDataType pdt) {
  for (auto it = cur_post_ifos.begin(); it != cur_post_ifos.end();it++) {

    Json::Value root;
    root["eventId"] = it->second.eventId;
    root["eventType"] = it->second.alarm_info;
    // 特殊存储
    root["waterColor"] = it->second.video_path;
    root["startTime"] = time_int64_to_string(it->second.start_time, "%Y-%m-%d %H:%M:%S");
    root["endTime"] = time_int64_to_string(it->second.end_time, "%Y-%m-%d %H:%M:%S");

    auto& nvr_ifo = it->second.cam_grp->nvr_ifo;
    PostInfo& pi = it->second;
    std::string writer_path, file_url;
    int ret = 0, try_cnt = 3;
    if (pdt == onlyPostVideo) {
      if (nvr_ifo.ok) { if ((ret = save_video_by_nvr(log_ifo, pi, nvr_ifo, writer_path)) != 0) { add_post_info(pi, onlyPostVideo); };}
      else  { ret = save_video_by_kit(log_ifo, pi, cfg_ifo->mediaDir, writer_path); }
      if (0 != ret) { continue; }

      while (try_cnt-- > 0 && file_url.empty()) { ret = post_file(file_url, cfg_ifo->httpServer, cfg_ifo->postAPI.uploadFile, writer_path, "name", 10, 2, log_ifo); }
      root["videoUrl"] = file_url;
    }
    else if (pdt == onlyPostImage) {
      while (try_cnt-- > 0 && file_url.empty()) { ret = post_file(file_url, cfg_ifo->httpServer, cfg_ifo->postAPI.uploadFile, it->second.img_path, "name", 10, 2, log_ifo); }
      root["imgUrl"] = file_url;
    }

    std::string ret_body, msg = root.toStyledString();
    if (log_ifo->log_level_1) spdlog::get("logger")->info("SmartBox::post_data_video_v2 post_data: \n{}", msg);

    std::string uri = cfg_ifo->postAPI.uploadAlarmSlow; 
    if (uri.empty()) { continue; }

    post_data_base(ret_body, msg, cfg_ifo->httpServer, uri, -1, 1, log_ifo);
  }
}


}		// namespace smartbox